package com.derbysoft.synxisadapter.dswitch2.translator.impl;

import com.derbysoft.core.utils.DateTimeUtils;
import com.derbysoft.remote.dswitch.generic.constants.UniqueIDType;
import com.derbysoft.remote.dswitch.hotel.constants.ChargeType;
import com.derbysoft.remote.dswitch.hotel.constants.ChargeUnit;
import com.derbysoft.remote.dswitch.hotel.dto.*;
import com.derbysoft.synxisadapter.commons.dto.synxis.NightlyRate;
import com.derbysoft.synxisadapter.commons.dto.synxis.RateType;
import com.derbysoft.synxisadapter.commons.dto.synxis.RoomRate;
import com.derbysoft.synxisadapter.commons.model.Channel;
import com.derbysoft.synxisadapter.commons.model.TaxPolicy;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.List;

public abstract class TranslateHelper {
    private static final int TAX_SCALE = 4;
    public static final int DELAY_ONE_DAY = -24;

    public static String find(List<HotelReservationIDDTO> hotelReservationIDs,
                              UniqueIDType uniqueIDType) {
        for (HotelReservationIDDTO hotelReservationID : hotelReservationIDs) {
            if (hotelReservationID.getResIDType() == uniqueIDType) {
                return hotelReservationID.getResIDValue();
            }
        }
        throw new IllegalStateException(String.format("HotelReservationID [%s] not found", uniqueIDType));
    }

    public static AvailRoomStayDTO filter(AvailRoomStayDTO unfilteredRoomStayDTO, int numberOfUnits) {
        if (unfilteredRoomStayDTO.getQuantity() == null
                || unfilteredRoomStayDTO.getQuantity() < numberOfUnits) {
            return null;
        }
        return unfilteredRoomStayDTO;
    }

    public static HotelReservationIDDTO ersp(String id) {
        return new HotelReservationIDDTO(UniqueIDType.ERSP, id);
    }

    public static BigDecimal setScale(BigDecimal amount) {
        return amount.setScale(2, RoundingMode.HALF_UP);
    }

    public static HotelReservationIDDTO findId(List<HotelReservationIDDTO> hotelReservationIDs,
                                               UniqueIDType uniqueIDType) {
        for (HotelReservationIDDTO hotelReservationID : hotelReservationIDs) {
            if (hotelReservationID.getResIDType() == uniqueIDType) {
                return hotelReservationID;
            }
        }
        return null;
    }

    public static RatePlanDTO setTax(StayDateRangeDTO stayDateRange, Channel channel, RatePlanDTO ratePlanDTO, RoomRate roomRate) {
        if (channel.isInclusiveTaxRate()) {
            return ratePlanDTO;
        }
        if (channel.getTaxPolicy() == TaxPolicy.PERCENT) {
            return setPercentTax(stayDateRange, ratePlanDTO, roomRate);
        }
        if (channel.getTaxPolicy() == TaxPolicy.FIXED) {
            return setFixedTax(stayDateRange, ratePlanDTO, roomRate);
        }
        return ratePlanDTO;
    }

    private static RatePlanDTO setPercentTax(StayDateRangeDTO stayDateRange, RatePlanDTO ratePlanDTO, RoomRate roomRate) {
        RatePlanDTO newRatePlanDTO = (RatePlanDTO) ratePlanDTO.clone();
        RateType rateType = roomRate.getRates().getRate().get(0);
        if (rateType.getTpaExtensions() != null &&
                !CollectionUtils.isEmpty(rateType.getTpaExtensions().getNightlyRate())) {
            BigDecimal totalPriceWithTaxAndFee = BigDecimal.ZERO;
            BigDecimal totalBasePrice = BigDecimal.ZERO;

            for (NightlyRate nightlyRate : rateType.getTpaExtensions().getNightlyRate()) {
                totalBasePrice = totalBasePrice.add(nightlyRate.getPrice());
                totalPriceWithTaxAndFee = totalPriceWithTaxAndFee.add(nightlyRate.getPriceWithTaxAndFee());
            }
            ChargeItemDTO chargeItemDTO = new ChargeItemDTO();
            chargeItemDTO.setType(ChargeType.PERCENT);
            chargeItemDTO.setStartDate(stayDateRange.getCheckInDate());
            chargeItemDTO.setEndDate(DateTimeUtils.addHours(stayDateRange.getCheckOutDate(), DELAY_ONE_DAY));
            chargeItemDTO.setUnit(ChargeUnit.PER_NIGHT);

            if (totalBasePrice.compareTo(BigDecimal.ZERO) == 0) {
                totalBasePrice = BigDecimal.ONE;
            }
            BigDecimal totalPercent = totalPriceWithTaxAndFee.subtract(totalBasePrice).divide(totalBasePrice, TAX_SCALE, RoundingMode.HALF_UP);
            if (totalPercent.compareTo(BigDecimal.ZERO) != 0) {
                chargeItemDTO.setValue(totalPercent);
                ArrayList<ChargeItemDTO> taxes = new ArrayList<ChargeItemDTO>();
                taxes.add(chargeItemDTO);
                newRatePlanDTO.setTaxes(taxes);
            }
        }
        return newRatePlanDTO;
    }

    private static RatePlanDTO setFixedTax(StayDateRangeDTO stayDateRange, RatePlanDTO ratePlanDTO, RoomRate roomRate) {
        RatePlanDTO newRatePlanDTO = (RatePlanDTO) ratePlanDTO.clone();
        RateType rateType = roomRate.getRates().getRate().get(0);
        if (rateType.getTpaExtensions() != null &&
                !CollectionUtils.isEmpty(rateType.getTpaExtensions().getNightlyRate())) {
            BigDecimal totalPriceWithTaxAndFee = BigDecimal.ZERO;
            BigDecimal totalBasePrice = BigDecimal.ZERO;

            for (NightlyRate nightlyRate : rateType.getTpaExtensions().getNightlyRate()) {
                totalBasePrice = totalBasePrice.add(nightlyRate.getPrice());
                totalPriceWithTaxAndFee = totalPriceWithTaxAndFee.add(nightlyRate.getPriceWithTaxAndFee());
            }
            ChargeItemDTO chargeItemDTO = new ChargeItemDTO();
            chargeItemDTO.setType(ChargeType.FIXED);
            chargeItemDTO.setStartDate(stayDateRange.getCheckInDate());
            chargeItemDTO.setEndDate(DateTimeUtils.addHours(stayDateRange.getCheckOutDate(), DELAY_ONE_DAY));
            chargeItemDTO.setUnit(ChargeUnit.PER_NIGHT);

            BigDecimal duration = stayDateRange.lengthOfStay() == 0 ? BigDecimal.ONE : BigDecimal.valueOf(stayDateRange.lengthOfStay());
            chargeItemDTO.setValue(setScale(totalPriceWithTaxAndFee.subtract(totalBasePrice).divide(duration, TAX_SCALE, RoundingMode.HALF_UP)));
            ArrayList<ChargeItemDTO> taxes = new ArrayList<ChargeItemDTO>();
            taxes.add(chargeItemDTO);
            newRatePlanDTO.setTaxes(taxes);
        }
        return newRatePlanDTO;
    }
}
